<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- jsonp请求 解决跨域问题-->
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script src="http://localhost:8888/get/jsonp?callback=lvchao"></script>
    <script>
        // 同源策略
        编程中的同源，比较的是两个url是否同源。
        主要看下面三个方面：
        1.协议是否相同（http  https  file）
        2.主机地址是否相同（www.xxx.com  127.0.0.1）
        3.端口（0~65535）（http默认端口是80；https默认端口是443；MySQL默认端口3306）

        协议、主机地址、端口组成一个“源”。
        如果两个url的协议、主机地址、端口都相同，那么这两个url是同源的，否则就是非同源。
        如果非同源，那么以下三种行为会受到限制：

        1.Cookie 无法操作
        2.DOM 无法操作
        3.Ajax请求无效 （请求可以发送，服务器也会处理这次请求，但是响应结果会被浏览器拦截）

        违反了同源策略的请求，叫做 跨域请求 。







        就是通过script标签发起请求, src写了一个地址，该地址给我们响应了一个字符串
        原生Js直接发送请求即可
        使用jQuery, ajax需要加一个属性 dataType: 'jsonp', 才能将设置为设置为jsonp请求; 不然会因为跨域而报错
        // 例
        $.ajax({
            method: 'GET',
            url: 'http://localhost:8888/get/jsonp',
            // 加一个属性dataType，就可以设置为jsonp请求;
            // jsonp请求是通过script标签发送的，不是通过xhr对象发送的;
            dataType: 'jsonp',  // 设置jsonp 发送方式
            success: function (res) {
                console.log(res);
            }
        });




        // !!前端 设置一全局函数，等待将来其他文件调用,并且在下方写一个script将函数名赋值并发送给后端
        function zhang(aaa) {
            console.log(aaa);
        }
        // !!后端   在后端接收书写路由
        app.get('/get/jsonp', function (req, res) {
            // 发送js代码
            // res.send("var num='我是后端定义的全局变量';");
            // res.send("alert('后端返回的数据！')");
            // 去执行前端有的全局函数
            // res.send('lvchao({name: "张三", age: 18})');
            // 把函数名，传给我
            let fnName = req.query.callback;
            setTimeout(function () {
                res.send(fnName + '({name: "李四", age: 21})');
            }, 1000);
        });
    </script>
    <!-- 自己创建一个script标签，模拟ajax请求，这就是jsonp的原理 -->
    <script src="http://localhost:8888/get/jsonp?callback=zhang"></script>








    CORS

    由于XHR对象被W3C标准化之后，提出了很多XHR Level2的新构想，其中新增了很多新方法（onload、response....）和CORS跨域资源共享。浏览器升级后开始支持CORS方案，从IE10开始支持。
    CORS方案，就是通过服务器设置响应头来实现跨域**。
    CORS才是解决跨域的真正解决方案。

    - 前端需要做什么？
    - 无需做任何事情，正常发送Ajax请求即可。
    - 后端需要做什么？
    - 需要加[响应头](https://developer.mozilla.org/zh-CN/docs/Glossary/CORS) 。或者使用第三方模块 **cors** 。

















    <!-- 防抖节流 -->
    <!-- 防抖   减少访问量,减轻服务器压力-->
    <script>
        let timer = 0;
        let input = document.querySelector('input')
        //  输入事件：input ;  键盘弹起事件：keyup;
        input.onkeyup = function () {
            // 用输出值，模拟向后端发送请求
            // console.log(this.value);
            // 防抖策略：利用定时器实现后端请求的发送，规定时间内再次出发，取消掉原有的请求;
            // 去掉原有定时器
            clearTimeout(timer);
            timer = setTimeout(() => {
                // 定时器里面的this指向window，为了获取事件源，改为使用箭头函数,this就可以指向上级事件源
                console.log(this.value);
            }, 1500);
        }
    </script>




    <!-- 节流 -->
    <!-- 例     小鸟跟随鼠标案例 -->
    <img src="./angel.gif" style="position: fixed;">
    <script>
        // 用原生js实现 - 鼠标在页面上移动，img跟随移动。
        let img = document.querySelector('img');
        // 全局变量接收定时器
        let timer = 0;
        document.onmousemove = function (e) {
            // 判断，上一次的定时器，有没有执行完毕，执行完毕在能执行下一次！
            if (timer !== 0) {
                return; // timer不等于0，就说明上一个定时器还没有执行完毕！
            }
            // 获取鼠标在页面中的坐标
            // console.log(e.pageX, e.pageY);
            timer = setTimeout(function () {
                console.log(1);
                img.style.top = e.pageY + 'px';
                img.style.left = e.pageX + 'px';
                // 执行完毕，一定要记得把timer设置为0；
                timer = 0;
            }, 20);
        }
    </script>







    <!-- module -->
    <script>
        // import 导入文件
        // // 方法1:  前缀后缀都不能省略
        import o1 from './02_导入文件.js';
        console.log(o1);
        // // 方法2: 前缀后缀都不能省略
        import { n1, s1, fn } from './02_导入文件.js';
        console.log(n1, s1, fn);
        import { n1, s1, fn as f1 } from './02_导入文件.js';// 可以赋值给别的变量,用as赋值
        console.log(n1, s1, f1());
        // 方法3: 前缀后缀都不能省略
        import './02_导入文件.js';// 要的是整个功能，不是返回的数据;






        // export 导出
        // 方法1: 导出单个
        let obj = { name: '张三', age: 18 };
        export default obj;
        // 方法2：导出多个
        export const n1 = 123;
        export let s1 = 'xyz';
        export const fn = function () {
            console.log('我是函数fn');
        }
        // 方法3: 导出所有  强调的是功能，不是导出的数据;
        console.log('去全聚德，吃一次烤鸭...');
        export default "鸭架子";
    </script>





    <!-- promise-->
    <script>
        // promise语法
        // es6 导入 fs 模块
        import fs from 'fs';
        // promise对象语法 - 参数是回调函数
        let p1 = new Promise((resolve, reject) => { // Promise这个构造函数里面，一定要放入异步代码！
            fs.readFile('./txt/a1.txt', 'utf8', (err, data) => {
                // resolve操作成功要他处理, reject操作失败用他处理
                if (err === null) {
                    resolve(data);
                } else {
                    reject(err);
                }
            });
        });
        // promise取值
        // 在 promise 对象身上，有两个方法，then()处理成功，catch()处理失败;
        p1.then((res) => {
            console.log(res);
        }).catch((err) => {
            console.log("文件读取失败: " + err.message);
        });




        // 解决回调地狱
        // 创建三个 promise 对象，每个对象读取一个文件
        let p1 = new Promise((resolve, reject) => {
            // 读取文件
            fs.readFile('./txt/a.txt', 'utf8', function (err, data) {
                resolve(data); // 只考虑成功，把成功的值跑出去；不考虑失败了
            });
        });
        let p2 = new Promise((resolve, reject) => {
            fs.readFile('./txt/b.txt', 'utf8', function (err, data) {
                resolve(data);// 只考虑成功
            });
        });
        let p3 = new Promise((resolve, reject) => {
            fs.readFile('./txt/c.txt', 'utf8', function (err, data) {
                resolve(data);// 只考虑成功
            });
        });
        // 按照顺序读取，解决回调地狱问题
        p1.then(res => {
            console.log(res);
            return p2; // 这个箭头函数的返回值，会赋值给第一个then();第二个then就是p2调用的
        }).then(res => {
            console.log(res);
            return p3;// 这个箭头函数的返回值，会赋值给第二个then();第三个then就是p3调用的
        }).then(res => {
            console.log(res);
        });
        // 直接读取 - 没有先后顺序
        p1.then(res => console.log(res)); // 不考虑catch();
        p2.then(res => console.log(res)); // 不考虑catch();
        p3.then(res => console.log(res)); // 不考虑catch();


        // promise同步异步
        // 同步
        console.log(1);
        // 创建promise对象 - 同步
        let p1 = new Promise((resolve, reject) => {
            resolve(111);
            console.log(2);
            console.log(3);
        });
        // 调用then(); - 异步执行
        p1.then(res => {
            console.log(res);
            console.log(4);
        });
        // 最后输出
        console.log(5);
    </script>




    <!-- 模块 -->
    <script>
        和fs一样都是可以用来读取文件
        fs 模块的返回值为 undefined
        then - fs 模块的返回值为 Promise对象


        // then-fs解决回调地狱
        // es6导入  
        import thenfs from 'then-fs';
        // then-fs    mysql    axios  等现有模块，返回的是Promise对象;
        thenfs.readFile('./txt/a.txt', 'utf8')
            .then(res => {
                console.log(res);
                return thenfs.readFile('./txt/b.txt', 'utf8');
            }).then(res => {
                console.log(res);
                return thenfs.readFile('./txt/c.txt', 'utf8');
            }).then(res => {
                console.log(res);
            });
    </script>



    <!-- async   await -->
    <script>
        // async 是异步的意思; 可以用户修饰一个函数，写到 function 的前面;
        async function fn() {
            return '我是fn函数的返回值'
        }// async 返回值为Promise 对象





        // await
        1.await只能出现在异步函数中！
        2.await能停止代码执行，让后面的同步代码，先执行;
        3.await后面跟随的必须是一个promise对象;
        4.await返回的是: Promise对象中的then()中的回调函数中的参数res(返回值);

        // 自定义一个异步函数
        async function f1() {
            return '函数f1';
        }
        async function fn() {
            // f1()返回的就是一个Promise对象;
            // await返回的是: Promise对象中的then()中的回调函数中的参数res;
            let aaa = await f1();
            console.log(aaa);
        }
        fn();
        // 最低端写一个同步代码
        console.log('同步代码');








        // async + await解决回调地狱
        // 还是需要 then-fs 模块;
        import thenfs from 'then-fs';
        // 定义一个异步函数, 函数里面就可以使用 await 了
        async function fn() {
            // 扩展: try ... catch ...  尝试执行某些可能出问题的代码，catch捕获异常
            try {
                // 用 await 关键字，剥离出 Promise对象中的then()中的回调函数中的res
                let t1 = await thenfs.readFile('./txt/a.txt', 'utf8');
                console.log(t1);
                let t2 = await thenfs.readFile('./txt/bb.txt', 'utf8');
                console.log(t2);
                let t3 = await thenfs.readFile('./txt/c.txt', 'utf8');
                console.log(t3);
            } catch (err) {
                console.log("文件读取出现异常: " + err.message);
            }
        }
        // 调用函数
        fn();
        console.log('我是同步代码');
    </script>




    <!-- 宏任务微任务 -->
    <script>
        // 异步 - 宏任务
        setTimeout(() => {
            //执行后 回调一个宏事件
            console.log('1')
        }, 0);
        // 同步
        console.log('2');
        // 构造函数中的参数是同步，then()的参数是异步; - 微任务
        new Promise((resolve) => {
            console.log('3');
            resolve()
        }).then(() => {
            console.log('4');
        }).then(() => {
            console.log('5')
        });
        // 同步
        console.log('6')
    // 2 3 6 4 5 1
    // 总结: Promise里面的then()虽然是异步任务，但是，他会比其他的异步任务优先执行，因为他是微任务！
    </script>
</body>

</html>